#include "document.h"

#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <qmath.h>

#include <QDebug>

Document::Document()
{
}

Document::Document(const QString &file)
{
    QFile inFile(file);
    name = QFileInfo(inFile.fileName()).baseName();

    inFile.open(QIODevice::ReadOnly);
    QStringList words(QString(inFile.readAll()).split(' ', QString::SkipEmptyParts));
    foreach(const QString &word, words)
        if(content.contains(word))
            content[word]++;
        else
            content.insert(word, 1);

    inFile.close();
}

double Document::similarityCos(const Document &doc) const
{
    return dot(doc) / (norm() * doc.norm());
}

double Document::similarityEuclid(const Document &doc) const
{
    float acc = 0.f;
    for(QHash<QString, float>::const_iterator it = weights.constBegin(); it != weights.constEnd(); it++)
        if(doc.weights.contains(it.key())){
            float diff = it.value() - doc.weights[it.key()];
            acc += diff * diff;
        }
        else{
            float val = it.value();
            acc += val * val;
        }
    for(QHash<QString, float>::const_iterator it = doc.weights.constBegin(); it != doc.weights.constEnd(); it++)
        if(!weights.contains(it.key())){
            float val = it.value();
            acc += val * val;
        }
    return -qSqrt(acc);
}

double Document::similarityManhattan(const Document &doc) const
{
    float acc = 0.f;
    for(QHash<QString, float>::const_iterator it = weights.constBegin(); it != weights.constEnd(); it++)
        if(doc.weights.contains(it.key()))
            acc += qAbs(it.value() - doc.weights[it.key()]);
        else
            acc += it.value();
    for(QHash<QString, float>::const_iterator it = doc.weights.constBegin(); it != doc.weights.constEnd(); it++)
        if(!weights.contains(it.key()))
            acc += it.value();
    return -acc;
}

double Document::similarityHamming(const Document &doc) const
{
    int acc = 0;
    for(QHash<QString, float>::const_iterator it = weights.constBegin(); it != weights.constEnd(); it++)
        if(doc.weights.contains(it.key()))
            acc++;
    return -(weights.size() + doc.weights.size() - 2 * acc);
}

double Document::similarityDice(const Document &doc) const
{
    int acc = 0;
    for(QHash<QString, float>::const_iterator it = weights.constBegin(); it != weights.constEnd(); it++)
        if(doc.weights.contains(it.key()))
            acc++;
    return double(acc) / (weights.size() + doc.weights.size());
}

double Document::similarityJaccard(const Document &doc) const
{
    int acc = 0;
    for(QHash<QString, float>::const_iterator it = weights.constBegin(); it != weights.constEnd(); it++)
        if(doc.weights.contains(it.key()))
            acc++;
    return double(acc) / (weights.size() + doc.weights.size() - acc);
}

QDataStream &operator<<(QDataStream &out, const Document &doc)
{
    out << doc.name
        << doc.weights;
    return out;
}

QDataStream &operator>>(QDataStream &in, Document &doc)
{
    in >> doc.name
       >> doc.weights;
    return in;
}


double Document::dot(const Document &doc) const
{
    float acc = 0.f;
    for(QHash<QString, float>::const_iterator it = weights.constBegin(); it != weights.constEnd(); it++)
        if(doc.weights.contains(it.key())){
            acc += it.value() * doc.weights[it.key()];
        }
    return acc;
}

double Document::norm() const
{
    float acc = 0.f;
    for(QHash<QString, float>::const_iterator it = weights.constBegin(); it != weights.constEnd(); it++)
        acc += it.value() * it.value();
    return qSqrt(acc);
}
